package com.atguigu.gulimall.ware.service.impl;
import com.alibaba.fastjson.TypeReference;
import com.atguigu.common.to.mq.OrderTo;
import com.atguigu.common.to.mq.StockDetailTo;
import com.atguigu.common.to.mq.StockLockedTo;
import com.atguigu.common.utils.R;
import com.atguigu.common.exception.NoStockException;
import com.atguigu.gulimall.ware.entity.WareOrderTaskDetailEntity;
import com.atguigu.gulimall.ware.entity.WareOrderTaskEntity;
import com.atguigu.gulimall.ware.feign.OrderFeignService;
import com.atguigu.gulimall.ware.feign.ProductFeignService;
import com.atguigu.gulimall.ware.service.WareOrderTaskDetailService;
import com.atguigu.gulimall.ware.service.WareOrderTaskService;
import com.atguigu.gulimall.ware.vo.OrderItemVo;
import com.atguigu.gulimall.ware.vo.OrderVo;
import com.atguigu.gulimall.ware.vo.SkuHasStockVo;
import com.atguigu.gulimall.ware.vo.WareSkuLockVo;
import com.rabbitmq.client.Channel;
import lombok.Data;
import org.springframework.amqp.core.Message;
import org.springframework.amqp.rabbit.annotation.RabbitHandler;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.io.IOException;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.atguigu.common.utils.PageUtils;
import com.atguigu.common.utils.Query;
import com.atguigu.gulimall.ware.dao.WareSkuDao;
import com.atguigu.gulimall.ware.entity.WareSkuEntity;
import com.atguigu.gulimall.ware.service.WareSkuService;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.StringUtils;
@Service("wareSkuService")
public class WareSkuServiceImpl extends ServiceImpl<WareSkuDao, WareSkuEntity> implements WareSkuService {
    @Autowired
    WareSkuDao wareSkuDao;
    @Autowired
    ProductFeignService productFeignService;
    @Autowired
    private RabbitTemplate rabbitTemplate;
    @Autowired
    private WareOrderTaskService orderTaskService;
    @Autowired
    private OrderFeignService orderFeignService;
    @Autowired
    private WareOrderTaskDetailService orderTaskDetailService;
    private void unLockStock(Long skuId,Long wareId,Integer  num,Long taskDetailId){
wareSkuDao.unLockStock(skuId,wareId,num,taskDetailId);
        WareOrderTaskDetailEntity entity= new WareOrderTaskDetailEntity();
        entity.setId(taskDetailId);
        entity.setLockStatus(2);//   变为已解锁
        orderTaskDetailService.updateById(entity);
    }
    @Override
    public PageUtils queryPage(Map<String, Object> params) {
        /**
         * skuId: 1
         * wareId: 2
         */
        QueryWrapper<WareSkuEntity> queryWrapper = new QueryWrapper<>();
        String skuId = (String) params.get("skuId");
        if(!StringUtils.isEmpty(skuId)){
            queryWrapper.eq("sku_id",skuId);
        }
        String wareId = (String) params.get("wareId");
        if(!StringUtils.isEmpty(wareId)){
            queryWrapper.eq("ware_id",wareId);
        }
        IPage<WareSkuEntity> page = this.page(
                new Query<WareSkuEntity>().getPage(params),
                queryWrapper
        );
        return new PageUtils(page);
    }
    @Override
    public void addStock(Long skuId, Long wareId, Integer skuNum) {
        //1、判断如果还没有这个库存记录新增
        List<WareSkuEntity> entities = wareSkuDao.selectList(new QueryWrapper<WareSkuEntity>().eq("sku_id", skuId).eq("ware_id", wareId));
        if(entities == null || entities.size() == 0){
            WareSkuEntity skuEntity = new WareSkuEntity();
            skuEntity.setSkuId(skuId);
            skuEntity.setStock(skuNum);
            skuEntity.setWareId(wareId);
            skuEntity.setStockLocked(0);
            //TODO 远程查询sku的名字，如果失败，整个事务无需回滚
            //1、自己catch异常
            //TODO 还可以用什么办法让异常出现以后不回滚？高级
            try {
                R info = productFeignService.info(skuId);
                Map<String,Object> data = (Map<String, Object>) info.get("skuInfo");
                if(info.getCode() == 0){
                    skuEntity.setSkuName((String) data.get("skuName"));
                }
            }catch (Exception e){
            }
            wareSkuDao.insert(skuEntity);
        }else{
            wareSkuDao.addStock(skuId,wareId,skuNum);
        }
    }
    @Override
    public List<SkuHasStockVo> getSkusHasStock(List<Long> skuIds) {  //  参数 是  每个sku的id

        List<SkuHasStockVo> collect = skuIds.stream().map(skuId -> {
            SkuHasStockVo vo = new SkuHasStockVo(); //  创建对象
//            查询当前sku的总库存量
            Long count= baseMapper.getSkuStock(skuId);  //   查一下还剩多少件货?
            vo.setSkuId(skuId);  //  赋值
            vo.setHasStock(count==null?false:count>0);  //  赋值
            return vo;
        }).collect(Collectors.toList());
        return collect;
    }
    /*
    为某个订单锁定库存
     */
    @Transactional
    @Override
    public Boolean orderLockStock(WareSkuLockVo vo) {
        WareOrderTaskEntity wareOrderTaskEntity = new WareOrderTaskEntity();  //  创建一个 库存工作单 对象
        wareOrderTaskEntity.setOrderSn(vo.getOrderSn());   //  设置  订单编号
        orderTaskService.save(wareOrderTaskEntity); //  保存到数据库
        List<OrderItemVo> locks = vo.getLocks();// 获取订单项
        List<SkuWareHasStock> collect = locks.stream().map(item -> {// 遍历
            SkuWareHasStock stock = new SkuWareHasStock();//
            Long skuId = item.getSkuId();
            stock.setSkuId(skuId);  //  设置sku_id
            stock.setNum(item.getCount());
            List<Long> wareIds = wareSkuDao.listWareIdHasSkuStock(skuId); // 返回的是仓库的id,就是所有 有这个sku的仓库的id
            stock.setWareIds(wareIds); //  设置
            return stock;
        }).collect(Collectors.toList());//   collect算是 订单项
        for (SkuWareHasStock hasStock: collect) {  //  然后遍历所有的订单项
            Boolean  skuStocked=false; //  这个 sku 有 库存吗? 先假定它没有
            Long skuId = hasStock.getSkuId();
            List<Long> wareIds = hasStock.getWareIds();  // 让我看看到底有没有,
            if(wareIds==null  &&   wareIds.size() ==0){    //   说明这款sku ,在所有的仓库都没有
        throw  new NoStockException(skuId);
            }
            for (Long wareId: wareIds) {   //  遍历所有 有这款sku 的仓库
                Long  count =  wareSkuDao.lockSkuStock(skuId,wareId,hasStock.getNum());
                if(count == 1){ //  如果某个仓库的返回值 ==1,说明这个仓库锁定这款sku成功了
            skuStocked=true; //  说明锁定成功了
                    //  然后  创建一个   库存工作单详情对象,记录这一次的锁定信息
                    WareOrderTaskDetailEntity wareOrderTaskDetailEntity = new WareOrderTaskDetailEntity(null,skuId,"",hasStock.getNum(),wareOrderTaskEntity.getId(),wareId,1); //   lock_status 1:已经锁定 2:已经解锁 3:扣减
                    orderTaskDetailService.save(wareOrderTaskDetailEntity);   //  保存到数据库
                    StockLockedTo stockLockedTo = new StockLockedTo(); //  这个又是什么对象?   创建它有什么用?
                    stockLockedTo.setId(wareOrderTaskEntity.getId());
                    StockDetailTo stockDetailTo = new StockDetailTo(); //  这个,其实 也是 库存单详情 对象
                    BeanUtils.copyProperties(wareOrderTaskDetailEntity,stockDetailTo);
                    //   只发id不行,防止回滚以后找不到数据
                    stockLockedTo.setDetail(stockDetailTo);
                    rabbitTemplate.convertAndSend("stock-event-exchange","stock.locked",stockLockedTo); //  这个是什么消息?  把刚才的库存工作单id,和库存工作单详情数据  发送到队列里面?

                    //   这个消息 发送到了 stock.delay.queue 队列
                    //    stock.delay.queue 队列 是一个死信队列,消息死后,会发送到stock-event-exchange交换机,然后根据routing-key:stock.release,发送到stock.release.stock.queue队列
                    // 谁来监听这个stock.release.stock.queue队列?   回答:  StockReleaseListener 类







                    break;// 然后退出锁定程序,就是说其他的仓库就不用锁了
                }else{//说明:当前仓库锁定失败,继续去锁定下一个仓库
                }
            }
            if(skuStocked == false){
                //  当前商品所有仓库都没有锁住
                throw  new NoStockException(skuId);
            }
        }
        return true;
    }
    @Override
    public void unlockStock(StockLockedTo stockLockedTo) {
            StockDetailTo detail = stockLockedTo.getDetail(); //   获取 库存工作单详情
            Long detailId = detail.getId();  //   获取 库存工作单详情id
            WareOrderTaskDetailEntity wareOrderTaskDetailEntity = orderTaskDetailService.getById(detailId); //   根据id再获取一下  库存工作单详情
            if(wareOrderTaskDetailEntity!=null){
                Long id= stockLockedTo.getId();  //  获取  库存工作单id
                WareOrderTaskEntity taskEntity= orderTaskService.getById(id);  // 根据id再获取一下库存工作单
                String orderSn = taskEntity.getOrderSn();  //获取订单编号
                R r = orderFeignService.getOrderStatus(orderSn);//根据订单编号获取订单状态.
                if(r.getCode()==0){
                    OrderVo orderVo = r.getData(new TypeReference<OrderVo>() {});
                    if(orderVo ==null  ||  orderVo.getStatus()   ==  4){// CANCLED(4,"已取消"),
                        //  订单已经被取消了,才能解锁库存
                        if(wareOrderTaskDetailEntity.getLockStatus()   ==  1){  //1:已经锁定 2:已经解锁 3:扣减
//  当前库存工作单详情,   状态1, 已锁定,但是未解锁才可以解锁.
                        unLockStock(detail.getSkuId(),detail.getWareId(),detail.getSkuNum(),detailId);
                        }
                    }
                }else {
                throw    new RuntimeException("远程服务失败");
                }
            }else{
        }
    }
/*
防止订单服务卡顿, 导致订单状态消息一直改不了,库存消息优先到期,查订单状态新建状态,什么都不做就走了
导致 卡顿的  订单,永远不能解锁库存
 */
    @Transactional
    @Override
    public void unlockStock(OrderTo orderTo) {
        String orderSn = orderTo.getOrderSn();  //  获取订单编号
        //  查一下最新的库存的解锁状态,防止重复解锁库存
        WareOrderTaskEntity task= orderTaskService.getOrderTaskByOrderSn(orderSn); //   获取一个 最新的  库存工作单  对象
        Long id = task.getId();   //  获取工作单 的id
        //按照工作单 找到所有 没有解锁的库存  进行解锁
        List<WareOrderTaskDetailEntity> entities = orderTaskDetailService.list(new QueryWrapper<WareOrderTaskDetailEntity>().eq("task_id",id)
                .eq("lock_status",1)
        );
        for (WareOrderTaskDetailEntity entity : entities) {
        unLockStock(entity.getSkuId(),entity.getWareId(),entity.getSkuNum(),entity.getId());
        }
    }
    @Data
    class  SkuWareHasStock{  //   这个又算是什么实体类?????
        private Long  skuId;  //  sku的id
        private Integer num;  // 要买的数量
        private List<Long>  wareIds;  //仓库id,为什么是数组?   难道所有 有 这款商品的 仓库?
    }
}